import { rectWidth, rectHeight, circleSize } from './const'
import { fittingString } from './index'
import G6 from '@antv/g6'

/**
 * 工具函数文件，平面全是二维平面，使用的是笛卡尔坐标，x，y网页坐标系，坐标系 ：
- X轴水平向右，Y轴垂直向下（网页坐标系）。
- 原点 (0, 0) 在左上角，即坐标平面的零点(0,0)在左上角
因为网页坐标系中Y轴向下，而传统数学坐标系中Y轴向上。在本工具函数中所有的角度和平面的定义为：使0度指向正东方向，90度指向正南方向，180度指向正西方向，270度指向正北方向。 角度为顺时针方向旋转
 */

/**
 * 寻找有效的前一个节点用于确定方向，因为有些用来控制路径变化的点太近了，无法确定有效方向
 * @param {Object} params - 参数对象
 * @param {Object} params.node - 当前节点
 * @param {Array} params.prevNodes - 前驱节点数组
 * @param {Object} params.findRelate - 寻找相近的，忽略方向
 * @returns {Object} 有效的前一个节点
 */
export function findValidPrevNode({ node, prevNodes, findRelate = false }) {
  const prevNode = prevNodes.find((n) => n.id === node.prevId || (findRelate && n.id === node.nextId))
  if (!prevNode) {
    return null // 如果没有前驱节点，返回 null
  }
  const distance = calculateDistance(prevNode, node)
  if (distance > 5) {
    //大于5基本可以排除带哦那些C指令曲线定义的点
    return prevNode // 如果距离大于5，返回当前前驱节点
  } else {
    // 否则，递归寻找前驱节点的前驱
    return findValidPrevNode({ node: prevNode, prevNodes, findRelate })
  }
}

/**
 * 寻找有效的后一个节点用于确定方向，因为有些用来控制路径变化的点太近了，无法确定有效方向
 * @param {Object} params - 参数对象
 * @param {Object} params.node - 当前节点
 * @param {Array} params.nextNodes - 后继节点数组
 * @param {Object} params.findRelate - 寻找相近的，忽略方向
 * @returns {Object} 有效的后一个节点
 */
export function findValidNextNode({ node, nextNodes, findRelate = false }) {
  const nextNode = nextNodes.find((n) => n.id === node.nextId || (findRelate && n.id === node.prevId))
  if (!nextNode) {
    return null // 如果没有前驱节点，返回 null
  }
  const distance = calculateDistance(node, nextNode)
  if (distance > 5) {
    return nextNode // 如果距离大于5，返回后继节点
  } else {
    // 否则，递归寻找后继节点的后继
    return findValidNextNode({ node: nextNode, nextNodes, findRelate })
  }
}

/**
 * 计算两点之间的距离
 * @param {{x: number, y: number}} p1 起始点
 * @param {{x: number, y: number}} p2 结束点
 * @returns {number} 两点距离
 */
export function calculateDistance(p1, p2) {
  const dx = p2.x - p1.x
  const dy = p2.y - p1.y
  return Math.sqrt(dx ** 2 + dy ** 2)
}

/**
 * 获取延长点坐标
 * @param {{x: number, y: number}} p1 起始点
 * @param {{x: number, y: number}} p2 结束点
 * @param {number} distance 延伸距离
 * @returns {{x:number, y: number, precent:number}} 新坐标点
 */
export function getExtendedPoint(p1, p2, distance) {
  // 计算方向向量
  const dx = p2.x - p1.x
  const dy = p2.y - p1.y

  // 计算原始线段长度
  const lengthAB = Math.sqrt(dx ** 2 + dy ** 2)

  // 处理重合点
  if (lengthAB === 0) return { x: p1.x, y: p1.y }

  // 计算单位向量
  const unitX = dx / lengthAB
  const unitY = dy / lengthAB
  const x = p1.x + unitX * distance
  const y = p1.y + unitY * distance
  const total = calculateDistance(p1, p2)
  const extended = calculateDistance(p1, { x, y })
  // 计算并返回
  return {
    x,
    y,
    precent: extended / total,
  }
}

/**
 * 计算点 C 到直线 AB 的垂直距离
 * @param {{x: number, y: number}} A 直线起点A
 * @param {{x: number, y: number}} B 直线起点B
 * @param {{x: number, y: number}} C 直线外的一个点
 * @returns {number} 点 C 到直线 AB 的垂直距离
 */
export function calculateVerticalDistance(A, B, C) {
  // 计算向量 AB 的分量
  const dxAB = B.x - A.x
  const dyAB = B.y - A.y

  // 处理 A 和 B 重合的情况
  if (dxAB === 0 && dyAB === 0) {
    // 返回C到A的距离
    const dx = C.x - A.x
    const dy = C.y - A.y
    return Math.sqrt(dx * dx + dy * dy)
  }

  // 计算向量 AC 的分量
  const dxAC = C.x - A.x
  const dyAC = C.y - A.y

  // 计算叉积的绝对值（AB × AC）
  const crossProduct = dxAB * dyAC - dyAB * dxAC
  const absCross = Math.abs(crossProduct)

  // 计算 AB 的模长
  const lengthAB = Math.sqrt(dxAB * dxAB + dyAB * dyAB)

  // 返回垂直距离
  return absCross / lengthAB
}

/**
 * 生成主干线路上的点坐标信息
 * 设计思路：
 * 1. 线段类型判断：
 *    - 水平线(isH)：起点和终点Y坐标相同
 *    - 垂直线(isV)：起点和终点X坐标相同
 *    - 斜线(isBias)：其他情况
 *
 * 2. 坐标计算策略：
 *    - 水平线：仅调整X坐标，Y保持不变
 *    - 垂直线：仅调整Y坐标，X保持不变
 *    - 斜线：使用线性插值同时调整X和Y坐标
 *
 * 3. 防碰撞机制：
 *    - 使用circleSize作为最小节点间距
 *    - 根据方向(E/W/N/S)动态调整偏移量
 *    - 斜线使用calculateDirection确保正确的方向判断
 *
 * @param {Object} params - 参数对象
 * @param {Object} params.target - 目标节点，包含x和y坐标
 * @param {number} params.target.x - 目标节点的x坐标
 * @param {number} params.target.y - 目标节点的y坐标
 * @param {Object} params.prev - 前驱节点，包含x和y坐标
 * @param {number} params.prev.x - 前驱节点的x坐标
 * @param {number} params.prev.y - 前驱节点的y坐标
 * @param {number} params.t - 位置百分比(0-1)，用于确定节点在线段上的具体位置
 * @returns {Object} 返回节点位置信息
 * @returns {number} x - 节点的x坐标
 * @returns {number} y - 节点的y坐标
 * @returns {boolean} isBias - 是否为斜线
 * @returns {string} dir - 方向('N'|'S'|'E'|'W'|'ES'|'WS'|'WN'|'EN')
 */
export function genPointpos({ target, prev, t }) {
  // 参数验证
  if (!target || !prev || typeof t !== 'number' || t < 0 || t > 1) {
    console.warn('genPointpos: Invalid parameters', { target, prev, t })
    return { x: 0, y: 0, isBias: false, dir: '' }
  }

  let x, y, dir

  // 确定线段类型
  const isH = target.y === prev.y
  const isV = target.x === prev.x
  const isBias = !isH && !isV

  // 1. 处理水平线
  if (isH) {
    // 处理水平线：保持Y坐标不变，根据方向调整X坐标
    const targetX = target.x
    const prevX = prev.x
    dir = targetX > prevX ? 'E' : 'W'

    // 根据方向计算偏移因子和坐标范围
    const factor = dir === 'E' ? -1 : 1
    const [left, right] = dir === 'E' ? [prevX, targetX] : [prevX, targetX]

    // 计算最终坐标
    x = left + (right - left) * t + factor * circleSize
    y = target.y
  }

  // 2. 处理垂直线
  else if (isV) {
    // 处理垂直线：保持X坐标不变，根据方向调整Y坐标
    const nextY = target.y
    const prevY = prev.y
    dir = nextY > prevY ? 'S' : 'N'

    // 根据方向计算偏移因子和坐标范围
    const factor = dir === 'S' ? -1 : 1
    const [left, right] = dir === 'S' ? [prevY, nextY] : [prevY, nextY]

    // 计算最终坐标
    x = target.x
    y = left + (right - left) * t + factor * circleSize
  }

  // 3. 处理斜线
  else if (isBias) {
    // 计算斜线方向并确定起点和终点
    dir = calculateDirection(prev, target)
    const [start, end] = [prev, target]

    // 使用线性插值计算斜线上的点坐标
    const dx = end.x - start.x
    const dy = end.y - start.y

    // 计算最终坐标（不添加偏移量，因为斜线节点通常不需要额外偏移）
    x = start.x + t * dx
    y = start.y + t * dy
  }
  // 确保返回值的完整性和类型正确性
  return {
    x: typeof x === 'number' ? x : 0,
    y: typeof y === 'number' ? y : 0,
    isBias: Boolean(isBias),
    dir: typeof dir === 'string' ? dir : '',
  }
}

/**
 * 计算两个节点之间的方向,指的是源节点朝向目标节点的方向
 * @param {{x: number, y: number, isStartNode?: boolean}} source - 源节点，包含x,y坐标，isStartNode表示是否是起始节点
 * @param {{x: number, y: number}} target - 目标节点，包含x,y坐标
 * @returns {'N'|'S'|'E'|'W'|'ES'|'WS'|'WN'|'EN'|undefined} 返回方向字符串：
 *   N - 北
 *   S - 南
 *   E - 东
 *   W - 西
 *   ES - 东南
 *   WS - 西南
 *   WN - 西北
 *   EN - 东北
 *   如果source参数为空则返回undefined
 */
export function calculateDirection(source, target) {
  if (!source) {
    console.error('calculateDirection:', source, target)
    return
  }
  let sourceX = source.x
  let sourceY = source.y
  if (source.isStartNode) {
    sourceX = source.x + rectWidth / 2
    sourceY = source.y + rectHeight / 2
  }
  const targetX = target.x
  const targetY = target.y

  // 水平线
  if (targetX === sourceX) {
    // 垂直线
    return targetY > sourceY ? 'S' : 'N'
  } else if (targetY === sourceY) {
    return targetX > sourceX ? 'E' : 'W'
  } else if (targetX > sourceX && targetY > sourceY) {
    // 东南方向
    return 'ES'
  } else if (targetX < sourceX && targetY > sourceY) {
    // 西南方向
    return 'WS'
  } else if (targetX < sourceX && targetY < sourceY) {
    // 西北方向
    return 'WN'
  } else if (targetX > sourceX && targetY < sourceY) {
    // 东北方向
    return 'EN'
  }
}

/**
 * 获取目标节点该启用哪个锚点给源节点连接边
 * @param {*} source 源节点
 * @param {*} target 目标节点
 * @returns {Object} 锚点信息
 */
export function findBestAnchorPoint(source, target) {
  const direction = calculateDirection(source, target)
  // 定义方向与锚点的映射
  const directionToAnchorMap = {
    N: [0.5, 0], // 北
    S: [0.5, 1], // 南
    E: [1, 0.5], // 东
    W: [0, 0.5], // 西
    ES: [1, 0.5], // 东南
    WS: [0, 0.5], // 西南
    WN: [0, 0.5], // 西北
    EN: [0, 0.5], // 东北
  }
  // 获取最佳锚点
  return directionToAnchorMap[direction] || []
}

/**
 * 根据源节点坐标、距离和角度计算新节点的位置
 * @param {Object} source - 源节点对象，包含x和y坐标
 * @param {number} source.x - 源节点的x坐标
 * @param {number} source.y - 源节点的y坐标
 * @param {number} distance - 新节点与源节点的距离（单位：像素）
 * @param {number} angle - 新节点相对于源节点的角度（单位：度，0度表示正东方向，90度表示正南方向）
 * @returns {Object} - 返回包含新节点坐标的对象
 * @returns {number} x - 新节点的x坐标
 * @returns {number} y - 新节点的y坐标
 * @example
 * const newPos = calculateNewCoordinate({x: 100, y: 100}, 50, 45);
 * // newPos = {x: 135.355, y: 64.644}
 */
export function calculateNewCoordinate(source, distance, angle) {
  const sourceX = source.x
  const sourceY = source.y
  // 将角度从度数转换为弧度
  const radians = (angle * Math.PI) / 180
  // 计算新坐标，注意在网页坐标系中：
  // - 0度指向正东方向，对应cos(0)=1, sin(0)=0
  // - 90度指向正南方向，对应cos(90°)=0, sin(90°)=1
  // - 在Y轴方向，正值向下，因此sin值不需要取反
  const newX = sourceX + distance * Math.cos(radians)
  const newY = sourceY + distance * Math.sin(radians)
  return { x: newX, y: newY }
}

export function findPaths(data) {
  const graph = {}
  const paths = {}

  // 构建邻接表
  data.edges.forEach((edge) => {
    if (!graph[edge.source]) {
      graph[edge.source] = []
    }
    graph[edge.source].push(edge.target)
  })

  // 深度优先搜索函数
  function dfs(nodeId, currentPath) {
    const node = data.nodes.find((n) => n.id === nodeId)
    const newPath = [...currentPath, node.id]

    // 记录路径
    paths[node.id] = paths[node.id] || []
    paths[node.id].push(newPath)

    // 如果当前节点没有子节点，停止搜索
    if (!graph[node.id] || graph[node.id].length === 0) {
      return
    }

    // 遍历所有子节点
    graph[node.id].forEach((neighborId) => {
      if (!currentPath.includes(neighborId)) {
        // 防止环
        dfs(neighborId, newPath)
      }
    })
  }

  // 从每个节点开始DFS
  data.nodes.forEach((node) => {
    if (!paths[node.id]) {
      // 只对未记录路径的节点执行DFS
      dfs(node.id, [])
    }
  })

  return paths
}
export function findLongestPathsInResult(pathsResult) {
  const longestPaths = {}
  // 遍历pathsResult中的每个键值对
  for (const key in pathsResult) {
    // eslint-disable-next-line no-prototype-builtins
    if (pathsResult.hasOwnProperty(key)) {
      // 如果当前键对应的数组长度大于之前记录的最长长度，则更新最长路径
      if (pathsResult[key].length > 0) {
        longestPaths[key] = pathsResult[key].reduce((longest, current) => {
          return current.length > longest.length ? current : longest
        })
      }
    }
  }

  return longestPaths
}

export function checkStartPoint(points = [], startXY = {}) {
  if (!points || !points.length || !startXY) {
    return { offsetX: 0, offsetY: 0 }
  }
  const firstPoint = points[0]
  if (!firstPoint || typeof firstPoint.x === 'undefined' || typeof firstPoint.y === 'undefined' || typeof startXY.x === 'undefined' || typeof startXY.y === 'undefined') {
    return { offsetX: 0, offsetY: 0 }
  }
  const offsetX = startXY.x - firstPoint.x
  const offsetY = startXY.y - firstPoint.y
  return {
    offsetX, // 水平偏差值
    offsetY, // 垂直偏差值
  }
}

/**
 * 检查节点是否与其他节点碰撞
 * @param {Object} node 当前节点
 * @param {Array} nodes 所有节点数组
 * @param {Number} minDistance 最小距离
 * @returns {Boolean} 是否碰撞
 */
export function checkNodeCollision(node, nodes, minDistance) {
  return nodes.some((otherNode) => {
    if (otherNode.id === node.id) return false

    // 根据节点类型调整安全距离
    let adjustedDistance = minDistance

    // 对于同一线路的节点使用更大的安全距离
    if (otherNode.lineNum === node.lineNum) {
      adjustedDistance *= 1.2
    }
    // 如果是分支节点与分支节点之间，增加安全距离
    if (node.isBrancheNode && otherNode.isBrancheNode) {
      adjustedDistance = minDistance * 1.2
    }

    // 如果是分支节点与主干节点之间，也增加安全距离
    if (node.isBrancheNode && !otherNode.isBrancheNode) {
      adjustedDistance = minDistance * 1.1
    }

    const distance = calculateDistance(node, otherNode)
    return distance < adjustedDistance
  })
}

// 辅助函数：检测两条边是否相交
export function checkEdgesIntersection(edge1, edge2, nodes) {
  // 获取边的端点
  const edge1Source = nodes.find((n) => n.id === edge1.source)
  const edge1Target = nodes.find((n) => n.id === edge1.target)
  const edge2Source = nodes.find((n) => n.id === edge2.source)
  const edge2Target = nodes.find((n) => n.id === edge2.target)

  if (!edge1Source || !edge1Target || !edge2Source || !edge2Target) {
    return false
  }

  // 检查是否是同一条线路的边
  if (edge1Source.lineNum === edge2Source.lineNum) {
    return false
  }

  // 使用更精确的边相交检测算法
  return doEdgesIntersect({ x: edge1Source.x, y: edge1Source.y }, { x: edge1Target.x, y: edge1Target.y }, { x: edge2Source.x, y: edge2Source.y }, { x: edge2Target.x, y: edge2Target.y })
}

/**
 * 检查两条边是否相交
 * @param {Object} p1 第一条边的起点
 * @param {Object} p2 第一条边的终点
 * @param {Object} p3 第二条边的起点
 * @param {Object} p4 第二条边的终点
 * @returns {Boolean} 是否相交
 */
export function doEdgesIntersect(p1, p2, p3, p4) {
  // 计算方向
  function direction(a, b, c) {
    return (c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)
  }

  // 检查点是否在线段上
  function onSegment(p, q, r) {
    return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y)
  }

  const d1 = direction(p3, p4, p1)
  const d2 = direction(p3, p4, p2)
  const d3 = direction(p1, p2, p3)
  const d4 = direction(p1, p2, p4)

  // 如果两条线段相交
  if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) {
    return true
  }

  // 特殊情况：共线
  if (d1 === 0 && onSegment(p3, p1, p4)) return true
  if (d2 === 0 && onSegment(p3, p2, p4)) return true
  if (d3 === 0 && onSegment(p1, p3, p2)) return true
  if (d4 === 0 && onSegment(p1, p4, p2)) return true

  return false
}

/**
 * 全局坐标管理系统，用于优化节点位置
 * @param {Array} nodes - 所有节点数组
 * @param {Array} edges - 所有边数组
 */
export function optimizeNodesPositions(nodes, edges) {
  // 创建全局坐标管理器
  const coordinateManager = new CoordinateManager()
  // 0. 记录每条路线的起始节点（矩形）作为禁止区域
  const startNodes = nodes.filter((node) => node.isStartNode)
  startNodes.forEach((node) => {
    // 考虑到起始节点是矩形，需要创建一个较大的禁止区域
    const nodeWidth = node.nodeSize[0]
    const nodeHeight = node.nodeSize[1]
    // 为起始节点创建一个略大于节点尺寸的禁止区域
    coordinateManager.addForbiddenArea({
      center: { x: node.x + nodeWidth / 2, y: node.y + nodeHeight / 2 },
      radius: Math.max(nodeWidth, nodeHeight) / 2 + 10, // 额外添加10px的缓冲区
    })
    // 0.1 记录每条路线的第一个业务节点作为禁止区域
    const firstBusinessNode = nodes.find((n) => n.prevId === node.id && !n.isStartNode && n.type === 'stationNode')
    if (firstBusinessNode) {
      // 为第一个业务节点创建禁止区域
      coordinateManager.addForbiddenArea({
        center: { x: firstBusinessNode.x, y: firstBusinessNode.y },
        radius: circleSize + 10, // 使用节点尺寸加上缓冲区作为禁止区域半径
      })
    }
  })

  // 1. 记录所有边的交叉点和禁止区域
  edges.forEach((edge1, i) => {
    edges.slice(i + 1).forEach((edge2) => {
      const intersection = findEdgeIntersection(edge1, edge2, nodes)
      if (intersection) {
        coordinateManager.addForbiddenArea({
          center: intersection,
          radius: 10, // 交叉点延伸半径
        })
      }
    })
  })

  // 2. 按照节点数量对路线进行排序
  const sortedLines = getSortedLines(nodes, edges)

  // 3. 优化主干节点位置
  sortedLines.forEach((line) => {
    // 这里第一个业务节点和路线的起始节点，他们都属于禁止移动入侵的区域， 跳过路线的起始节点和第一个业务节点
    const mainNodes = line.nodes.filter((node) => !node.isBranchNode && node.type === 'stationNode' && !node.isStartNode && node.prevId !== line.nodes.find((n) => n.isStartNode).id)
    mainNodes.forEach((node) => {
      if (!coordinateManager.isPositionValid(node)) {
        const newPosition = findOptimalPosition(node, coordinateManager, line.nodes)
        if (newPosition) {
          node.x = newPosition.x
          node.y = newPosition.y
          coordinateManager.addOccupiedPosition(newPosition)
        }
      } else {
        coordinateManager.addOccupiedPosition(node)
      }
    })
  })

  // 4. 优化分支节点位置
  sortedLines.forEach((line) => {
    const branchNodes = line.nodes.filter((node) => node.isBranchNode && node.type === 'stationNode')
    branchNodes.forEach((node) => {
      if (!coordinateManager.isPositionValid(node)) {
        const newPosition = findOptimalPosition(node, coordinateManager)
        if (newPosition) {
          node.x = newPosition.x
          node.y = newPosition.y
          coordinateManager.addOccupiedPosition(newPosition)
        }
      } else {
        coordinateManager.addOccupiedPosition(node)
      }
    })
  })
}

/**
 * 坐标管理器类
 */
class CoordinateManager {
  constructor() {
    this.forbiddenAreas = []
    this.occupiedPositions = []
    this.minDistance = 20 // 节点之间的最小安全距离
  }

  addForbiddenArea(area) {
    this.forbiddenAreas.push(area)
    // console.log('addForbiddenArea', area)
  }

  addOccupiedPosition(position) {
    this.occupiedPositions.push(position)
    console.log('addOccupiedPosition', position)
  }

  isPositionValid(position) {
    // 检查是否在禁止区域内
    const inForbiddenArea = this.forbiddenAreas.some((area) => {
      return calculateDistance(position, area.center) <= area.radius
    })
    if (inForbiddenArea) return false

    // 检查是否与其他节点距离过近
    const tooCloseToOthers = this.occupiedPositions.some((pos) => {
      return calculateDistance(position, pos) < this.minDistance
    })
    return !tooCloseToOthers
  }
}

/**
 * 查找两条边的交叉点
 * @param {Object} edge1 - 第一条边
 * @param {Object} edge2 - 第二条边
 * @param {Array} nodes - 所有节点数组
 * @returns {Object|null} 交叉点坐标或null
 */
function findEdgeIntersection(edge1, edge2, nodes) {
  const start1 = nodes.find((n) => n.id === edge1.source)
  const end1 = nodes.find((n) => n.id === edge1.target)
  const start2 = nodes.find((n) => n.id === edge2.source)
  const end2 = nodes.find((n) => n.id === edge2.target)

  // 线段相交检测
  const denominator = (end2.y - start2.y) * (end1.x - start1.x) - (end2.x - start2.x) * (end1.y - start1.y)

  if (denominator === 0) return null

  const ua = ((end2.x - start2.x) * (start1.y - start2.y) - (end2.y - start2.y) * (start1.x - start2.x)) / denominator
  const ub = ((end1.x - start1.x) * (start1.y - start2.y) - (end1.y - start1.y) * (start1.x - start2.x)) / denominator

  if (ua < 0 || ua > 1 || ub < 0 || ub > 1) return null

  return {
    x: start1.x + ua * (end1.x - start1.x),
    y: start1.y + ua * (end1.y - start1.y),
  }
}

/**
 * 获取按节点数量排序的路线
 * @param {Array} nodes - 所有节点数组
 * @param {Array} edges - 所有边数组
 * @returns {Array} 排序后的路线数组
 */
function getSortedLines(nodes, edges) {
  const lines = []
  const nodeGroups = {}

  // 按照lineNum分组
  nodes.forEach((node) => {
    if (!nodeGroups[node.lineNum]) {
      nodeGroups[node.lineNum] = []
    }
    nodeGroups[node.lineNum].push(node)
  })

  // 转换为路线数组并排序
  Object.entries(nodeGroups).forEach(([lineNum, nodes]) => {
    lines.push({
      lineNum: parseInt(lineNum),
      nodes,
      nodeCount: nodes.length,
    })
  })

  return lines.sort((a, b) => b.nodeCount - a.nodeCount)
}

/**
 * 为节点找到最优位置
 * @param {Object} node - 需要优化位置的节点
 * @param {CoordinateManager} coordinateManager - 坐标管理器实例
 * @returns {Object|null} 最优位置或null
 */
function findOptimalPosition(node, coordinateManager, lineNodes) {
  console.log('findOptimalPosition', node.lable, node)
  // 主干节点必须在路线上，使用原有的genPointpos逻辑
  if (!node.isBrancheNode && node.extraInfo) {
    const { prev, target, t } = node.extraInfo
    if (prev && target && typeof t === 'number') {
      // 首先尝试原始位置
      const initialPosition = genPointpos({ prev, target, t })
      if (coordinateManager.isPositionValid(initialPosition)) {
        return initialPosition
      }

      // 计算线段总长度
      const segmentLength = Math.sqrt(Math.pow(target.x - prev.x, 2) + Math.pow(target.y - prev.y, 2))
      // 根据线段长度动态调整搜索步长
      const deltaT = Math.min(0.02, 20 / segmentLength) // 较小的步长以获得更精确的位置
      const maxDelta = Math.min(0.2, 100 / segmentLength) // 搜索范围根据线段长度调整

      // 获取同一区间内的所有节点
      // 1. 找到当前节点所在的原始路线节点区间
      const baseNode = lineNodes.find((n) => !n.isBrancheNode && n.startPercent <= node.extraInfo.percent && n.endPercent >= node.extraInfo.percent)

      if (!baseNode) return initialPosition

      // 2. 获取同一区间内的所有主干节点
      const segmentNodes = lineNodes
        .filter((n) => !n.isBrancheNode && n.extraInfo && n.extraInfo.percent >= baseNode.startPercent && n.extraInfo.percent <= baseNode.endPercent && n.lineNum === node.lineNum)
        .sort((a, b) => a.extraInfo.percent - b.extraInfo.percent)

      // 计算当前节点在区间内的索引
      const currentIndex = segmentNodes.findIndex((n) => n.id === node.id)
      const totalNodes = segmentNodes.length

      // 交替向两个方向搜索，优先考虑更接近原始位置的点
      for (let delta = deltaT; delta <= maxDelta; delta += deltaT) {
        // 向前搜索
        if (t - delta >= 0) {
          const position = genPointpos({ prev, target, t: t - delta })
          if (coordinateManager.isPositionValid(position)) {
            // 更新节点的t值
            node.extraInfo.t = t - delta
            return position
          }
        }
        // 向后搜索
        if (t + delta <= 1) {
          const position = genPointpos({ prev, target, t: t + delta })
          if (coordinateManager.isPositionValid(position)) {
            // 更新节点的t值
            node.extraInfo.t = t + delta
            return position
          }
        }
      }

      // 如果在常规范围内没找到位置，返回原始位置
      // 这确保了主干节点一定在线上，即使可能与其他节点有轻微重叠
      return initialPosition
    }
  }

  // 分支节点使用改进的螺旋式搜索策略
  const baseRadius = 20 // 基础搜索半径
  const radiusStep = 10 // 每圈半径增加量
  const searchSteps = 12 // 每圈的搜索点数，增加搜索点以提高精度
  const maxAttempts = 6 // 最大搜索圈数

  // 随机选择起始角度，避免所有节点都从同一位置开始搜索
  const startAngle = Math.random() * Math.PI * 2

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const currentRadius = baseRadius + (attempt - 1) * radiusStep

    for (let i = 0; i < searchSteps; i++) {
      // 在起始角度的基础上进行搜索
      const angle = startAngle + ((i * 360) / searchSteps) * (Math.PI / 180)
      const position = {
        x: node.x + currentRadius * Math.cos(angle),
        y: node.y + currentRadius * Math.sin(angle),
      }

      if (coordinateManager.isPositionValid(position)) {
        return position
      }
    }
  }

  // 如果没找到有效位置，返回原始位置
  return {
    x: node.x,
    y: node.y,
  }
}

/**
 * 优化文本标签位置，避免标签重叠和与边的碰撞
 * @param {Array} nodes - 节点数组
 * @param {Array} edges - 边数组
 */
export function optimizeTextLabelsPositions(nodes, edges = []) {
  // 文本标签管理器，用于记录和管理所有标签的位置信息
  class TextLabelManager {
    constructor() {
      this.occupiedAreas = []
      this.safetyMargin = 5 // 标签之间的安全距离
    }

    // 添加已占用区域
    addOccupiedArea(box) {
      if (box) {
        this.occupiedAreas.push(box)
      }
    }

    // 检查位置是否有效（不与其他标签重叠，且不与当前节点的圆形形状碰撞）
    isPositionValid(box, node) {
      if (!box) return false
      // 检查与其他标签的碰撞
      if (this.occupiedAreas.some((area) => checkBoxesCollision(box, area))) {
        return false
      }
      // 检查与当前节点圆形的碰撞
      if (node && !node.isStartNode) {
        const nodeRadius = circleSize / 4
        const nodeBox = {
          left: node.x - nodeRadius,
          right: node.x + nodeRadius,
          top: node.y - nodeRadius,
          bottom: node.y + nodeRadius,
        }
        if (checkBoxesCollision(box, nodeBox)) {
          return false
        }
      }
      return true
    }

    // 计算区域空间密度
    calculateSpaceDensity(box, radius = 50) {
      let density = 0
      this.occupiedAreas.forEach((area) => {
        const centerX = (box.left + box.right) / 2
        const centerY = (box.top + box.bottom) / 2
        const areaX = (area.left + area.right) / 2
        const areaY = (area.top + area.bottom) / 2
        const distance = Math.sqrt((centerX - areaX) ** 2 + (centerY - areaY) ** 2)
        if (distance <= radius) {
          density += 1 / (distance + 1) // 添加1避免除以0
        }
      })
      return density
    }

    // 寻找最优位置
    findOptimalPosition(node, currentBox, edges) {
      // 定义搜索范围和步长
      const searchRadius = 50
      const angleStep = 45 // 每45度搜索一个方向
      const distanceStep = 10 // 每次移动10像素
      const maxDistance = 30 // 文本标签与节点的最大距离
      let bestPosition = null
      let lowestDensity = Infinity

      // 在不同角度和距离上搜索
      for (let angle = 0; angle < 360; angle += angleStep) {
        for (let distance = distanceStep; distance <= Math.min(searchRadius, maxDistance); distance += distanceStep) {
          const dx = Math.cos((angle * Math.PI) / 180) * distance
          const dy = Math.sin((angle * Math.PI) / 180) * distance

          const adjustedBox = {
            ...currentBox,
            left: currentBox.left + dx,
            right: currentBox.right + dx,
            top: currentBox.top + dy,
            bottom: currentBox.bottom + dy,
          }

          // 检查位置是否有效（不与其他标签重叠，不与当前节点圆形碰撞，不与边碰撞）
          if (this.isPositionValid(adjustedBox, node) && !checkTextEdgeCollision(node, adjustedBox, edges)) {
            const density = this.calculateSpaceDensity(adjustedBox)
            if (density < lowestDensity) {
              lowestDensity = density
              bestPosition = { x: dx, y: dy }
            }
          }
        }
      }

      // 如果找到了合适的位置，返回该位置
      if (bestPosition) {
        return bestPosition
      }

      return null
    }
  }

  // 计算文本标签的边界框
  const calculateTextBoundingBox = (node) => {
    if (!node.textAttrs || !node.label) return null

    const { x, y, textAlign, textBaseline, fontSize } = node.textAttrs
    const text = node.textAttrs.text || node.label

    // 计算文本宽度，考虑中英文字符
    const textWidth = text.split('').reduce((width, letter) => {
      if (/[\u4E00-\u9FA5]/.test(letter)) {
        return width + fontSize // 中文字符
      } else {
        return width + G6.Util.getLetterWidth(letter, fontSize) // 英文字符
      }
    }, 0)
    const textHeight = fontSize * 1.2

    // 根据对齐方式计算边界框
    let left, top, right, bottom

    // 水平对齐
    if (textAlign === 'left') {
      left = node.x + x
      right = node.x + x + textWidth
    } else if (textAlign === 'right') {
      left = node.x + x - textWidth
      right = node.x + x
    } else {
      // center
      left = node.x + x - textWidth / 2
      right = node.x + x + textWidth / 2
    }

    // 垂直对齐
    if (textBaseline === 'top') {
      top = node.y + y
      bottom = node.y + y + textHeight
    } else if (textBaseline === 'bottom') {
      top = node.y + y - textHeight
      bottom = node.y + y
    } else {
      // middle
      top = node.y + y - textHeight / 2
      bottom = node.y + y + textHeight / 2
    }

    return { left, top, right, bottom, width: textWidth, height: textHeight }
  }

  // 检测两个边界框是否碰撞
  const checkBoxesCollision = (box1, box2) => {
    if (!box1 || !box2) return false
    // 考虑安全距离
    const safetyMargin = 5
    return (
      box1.left - safetyMargin < box2.right + safetyMargin &&
      box1.right + safetyMargin > box2.left - safetyMargin &&
      box1.top - safetyMargin < box2.bottom + safetyMargin &&
      box1.bottom + safetyMargin > box2.top - safetyMargin
    )
  }

  // 检测文本标签是否与边碰撞
  const checkTextEdgeCollision = (node, textBox, edges) => {
    if (!textBox) return false

    // 获取相关边
    const relatedEdges = edges.filter((edge) => edge.source === node.id || edge.target === node.id)

    for (const edge of relatedEdges) {
      const sourceNode = nodes.find((n) => n.id === edge.source)
      const targetNode = nodes.find((n) => n.id === edge.target)
      if (!sourceNode || !targetNode) continue

      // 检查水平线路
      const isHorizontalLine = Math.abs(sourceNode.y - targetNode.y) < 1
      if (isHorizontalLine) {
        const lineY = sourceNode.y
        const safetyMargin = 5

        if (textBox.top - safetyMargin <= lineY && textBox.bottom + safetyMargin >= lineY) {
          const lineLeft = Math.min(sourceNode.x, targetNode.x)
          const lineRight = Math.max(sourceNode.x, targetNode.x)

          if (!(textBox.right < lineLeft || textBox.left > lineRight)) {
            return true
          }
        }
      }

      // 检查端点和线段碰撞
      const isPointInBox = (x, y) => x >= textBox.left && x <= textBox.right && y >= textBox.top && y <= textBox.bottom

      if (isPointInBox(sourceNode.x, sourceNode.y) || isPointInBox(targetNode.x, targetNode.y)) {
        return true
      }

      // 检查线段与文本框边界的交叉
      const boxEdges = [
        [
          { x: textBox.left, y: textBox.top },
          { x: textBox.right, y: textBox.top },
        ],
        [
          { x: textBox.right, y: textBox.top },
          { x: textBox.right, y: textBox.bottom },
        ],
        [
          { x: textBox.right, y: textBox.bottom },
          { x: textBox.left, y: textBox.bottom },
        ],
        [
          { x: textBox.left, y: textBox.bottom },
          { x: textBox.left, y: textBox.top },
        ],
      ]

      for (const boxEdge of boxEdges) {
        if (doEdgesIntersect({ x: sourceNode.x, y: sourceNode.y }, { x: targetNode.x, y: targetNode.y }, boxEdge[0], boxEdge[1])) {
          return true
        }
      }
    }

    return false
  }

  // 初始化文本标签管理器
  const labelManager = new TextLabelManager()

  // 按照路线节点数量和节点优先级进行排序
  const sortedLines = getSortedLines(nodes, edges)
  const sortedNodes = []

  // 首先将每条路线的节点按优先级排序后添加到总排序列表中
  sortedLines.forEach((line) => {
    const lineNodes = line.nodes.sort((a, b) => {
      if (a.isStartNode) return -1
      if (b.isStartNode) return 1
      if (!a.isBrancheNode && b.isBrancheNode) return -1
      if (a.isBrancheNode && !b.isBrancheNode) return 1
      return 0
    })
    sortedNodes.push(...lineNodes)
  })

  // 处理每个节点的标签位置
  sortedNodes.forEach((node) => {
    if (node.label === '') return

    // 获取同一线路的节点
    const lineNumNodes = nodes.filter((n) => n.lineNum === node.lineNum)
    const prevNode = !node.isStartNode ? findValidPrevNode({ node, prevNodes: lineNumNodes, findRelate: true }) : {}

    // 计算初始文本属性
    node.textAttrs = calculateTextAttrs({ node, prevNode, lineNumNodes, nodes })

    // 计算文本边界框
    const textBox = calculateTextBoundingBox(node)
    if (!textBox) return

    // 检查是否需要调整位置
    if (!labelManager.isPositionValid(textBox, node) || checkTextEdgeCollision(node, textBox, edges)) {
      // 第一步：在节点附近搜索空旷区域
      const searchRadius = 100 // 增大搜索半径以找到更多可能的空旷区域
      const adjustment = labelManager.findOptimalPosition(node, textBox, edges)

      if (adjustment) {
        // 找到合适的空旷区域，更新文本属性
        node.textAttrs.x += adjustment.x
        node.textAttrs.y += adjustment.y

        // 重新计算边界框并添加到已占用区域
        const newBox = calculateTextBoundingBox(node)
        if (newBox) {
          labelManager.addOccupiedArea(newBox)
        }
      } else {
        // 第二步：如果找不到合适的空旷区域，尝试不同方向的位置调整
        const maxOffset = 25 // 最大偏移距离
        const directions = [
          { dx: 0, dy: -maxOffset }, // 上
          { dx: 0, dy: maxOffset }, // 下
          { dx: maxOffset, dy: 0 }, // 右
          { dx: -maxOffset, dy: 0 }, // 左
          { dx: maxOffset * 0.7, dy: maxOffset * 0.7 }, // 右下
          { dx: -maxOffset * 0.7, dy: maxOffset * 0.7 }, // 左下
          { dx: maxOffset * 0.7, dy: -maxOffset * 0.7 }, // 右上
          { dx: -maxOffset * 0.7, dy: -maxOffset * 0.7 }, // 左上
        ]

        let found = false
        for (const dir of directions) {
          const adjustedBox = {
            ...textBox,
            left: textBox.left + dir.dx,
            right: textBox.right + dir.dx,
            top: textBox.top + dir.dy,
            bottom: textBox.bottom + dir.dy,
          }

          if (labelManager.isPositionValid(adjustedBox, node) && !checkTextEdgeCollision(node, adjustedBox, edges)) {
            node.textAttrs.x += dir.dx
            node.textAttrs.y += dir.dy
            found = true
            const newBox = calculateTextBoundingBox(node)
            if (newBox) {
              labelManager.addOccupiedArea(newBox)
            }
            break
          }
        }

        // 第三步：如果位置调整失败，才考虑缩短文本
        if (!found) {
          const maxWidth = node.isStartNode ? rectWidth - 30 : node.isBrancheNode ? 60 : 77
          let currentWidth = Math.min(maxWidth, 150)
          let shorterText = node.label

          // 逐步缩短文本直到找到合适的位置或达到最小长度
          while (currentWidth > 30 && shorterText.length > 2) {
            // 保证至少保留2个字符
            shorterText = fittingString(node.label, currentWidth, node.textAttrs.fontSize, true)
            node.textAttrs.text = shorterText
            node.textAttrs.fitText = shorterText
            const newBox = calculateTextBoundingBox(node)
            if (newBox && labelManager.isPositionValid(newBox, node) && !checkTextEdgeCollision(node, newBox, edges)) {
              labelManager.addOccupiedArea(newBox)
              break
            }
            currentWidth *= 0.8 // 每次缩短20%
          }
        }
      }
    } else {
      // 位置有效，直接添加到已占用区域
      labelManager.addOccupiedArea(textBox)
    }
  })
}

/**
 * 根据节点走向设置当前节点的文字属性
 * @param {*} node
 * @param {*} prevNode
 * @returns
 */
function calculateTextAttrs({ node = {}, prevNode = {}, nodes = [] }) {
  const { nodeSize, isStartNode, shapeType, prevId, label, lineNum, isLastNode, isBrancheNode } = node
  const vGap = 10 // 竖向间距增加
  const hGap = 6 // 水平间距增加
  const direction = !isStartNode ? calculateDirection(prevNode, node) : ''

  // 默认文本属性
  const textAttrs = {
    x: shapeType === 'rect' ? nodeSize[0] / 2 : 0,
    y: shapeType === 'rect' ? nodeSize[1] / 2 - 5 : nodeSize / 2 + hGap,
    fontSize: 14,
    fill: '#333',
    text: '',
    textAlign: 'center', // 设置文本内容的当前对齐方式
    textBaseline: 'top', // 设置在绘制文本时使用的当前文本基线
  }

  // 根据节点类型调整最大宽度
  const maxWidth = isStartNode ? rectWidth - 30 : isBrancheNode ? 60 : 77
  const text = fittingString(label, maxWidth, textAttrs.fontSize, true)
  textAttrs.text = text
  textAttrs.fitText = text
  if (!isStartNode) {
    // 获取分支节点与主干的关系方向
    const mainTrunkDirection = node.mainTrunkDirection || direction
    const parentNode = nodes.find((n) => n.id === node.parentNodeId)

    // 计算周围节点密度，决定标签放置位置
    const surroundingNodes = nodes.filter((n) => n.id !== node.id && calculateDistance(node, n) < 100)

    // 根据周围节点的分布情况选择最佳的标签位置
    const positions = [
      { side: 'right', count: 0, priority: 0 },
      { side: 'left', count: 0, priority: 0 },
      { side: 'top', count: 0, priority: 0 },
      { side: 'bottom', count: 0, priority: 0 },
    ]

    // 计算各个方向的节点密度
    surroundingNodes.forEach((n) => {
      const dx = n.x - node.x
      const dy = n.y - node.y

      if (Math.abs(dx) > Math.abs(dy)) {
        // 水平方向
        if (dx > 0) positions[0].count++ // 右侧
        else positions[1].count++ // 左侧
      } else {
        // 垂直方向
        if (dy < 0) positions[2].count++ // 上方
        else positions[3].count++ // 下方
      }
    })

    // 根据主干方向调整优先级
    // 优先选择与主干方向垂直的方向放置标签
    if (mainTrunkDirection) {
      switch (mainTrunkDirection) {
        case 'N':
        case 'S':
          // 主干是南北方向，优先考虑东西方向放标签
          positions[0].priority -= 2 // 右侧优先
          positions[1].priority -= 2 // 左侧优先
          break
        case 'E':
        case 'W':
          // 主干是东西方向，优先考虑南北方向放标签
          positions[2].priority -= 2 // 上方优先
          positions[3].priority -= 2 // 下方优先
          break
        case 'ES':
        case 'WN':
          // 东南或西北方向，优先考虑西南或东北方向
          positions[1].priority -= 1 // 左侧
          positions[3].priority -= 1 // 下方
          break
        case 'WS':
        case 'EN':
          // 西南或东北方向，优先考虑东南或西北方向
          positions[0].priority -= 1 // 右侧
          positions[2].priority -= 1 // 上方
          break
      }
    }

    // 如果有父节点，避免标签放在父节点方向
    if (parentNode) {
      const dx = parentNode.x - node.x
      const dy = parentNode.y - node.y

      if (Math.abs(dx) > Math.abs(dy)) {
        // 父节点在水平方向
        if (dx > 0) positions[0].priority += 3 // 避免右侧
        else positions[1].priority += 3 // 避免左侧
      } else {
        // 父节点在垂直方向
        if (dy < 0) positions[2].priority += 3 // 避免上方
        else positions[3].priority += 3 // 避免下方
      }
    }

    // 综合考虑节点密度和优先级
    positions.forEach((pos) => {
      pos.score = pos.count * 2 + pos.priority
    })

    // 选择得分最低的方向放置标签
    positions.sort((a, b) => a.score - b.score)
    const bestPosition = positions[0].side
    textAttrs.bestPosition = bestPosition
    let ty
    switch (bestPosition) {
      case 'right':
        textAttrs.textAlign = 'left'
        textAttrs.textBaseline = 'middle'
        textAttrs.x = nodeSize / 2 + hGap
        textAttrs.y = 0
        if (['E', 'W'].includes(mainTrunkDirection)) {
          // 如果是水平 则需要调整y不允许重叠在水平路线上
          textAttrs.y = -vGap
        }
        break
      case 'left':
        textAttrs.textAlign = 'right'
        textAttrs.textBaseline = 'middle'
        textAttrs.x = -hGap
        textAttrs.y = 0
        if (['E', 'W'].includes(mainTrunkDirection)) {
          // 如果是水平 则需要调整y不允许重叠在水平路线上
          textAttrs.y = -vGap
        }
        break
      case 'top':
        textAttrs.textAlign = 'center'
        textAttrs.textBaseline = 'bottom'
        ty = -vGap
        textAttrs.x = 0
        textAttrs.y = ty
        // 如果前一个节点也是 一样的模式  那么需要和他对立  例如上面的话 这个就放到下面 'bottom',
        if (['top'].includes(prevNode?.textAttrs?.bestPosition) && prevNode?.textAttrs?.y === ty) {
          textAttrs.y = nodeSize + vGap
        }
        break
      case 'bottom':
        textAttrs.textAlign = 'center'
        textAttrs.textBaseline = 'top'
        textAttrs.x = 0
        ty = nodeSize / 2 + vGap
        textAttrs.y = ty
        // 如果前一个节点也是 一样的模式  那么需要和他对立  例如下面的话 这个就放到上面 'top'
        if (['bottom'].includes(prevNode?.textAttrs?.bestPosition) && prevNode?.textAttrs?.y === ty) {
          textAttrs.y = -vGap
        }
        break
    }
  }
  return textAttrs
}

/**
 * 处理还没生成坐标的分支节点
 * @param {*} doneNodes 已经有坐标了的节点
 * @param {*} beRelatNodes 还没有坐标的节点
 * @param {*} doneDataIds 已经有坐标了的节点dataId数组
 * @param {*} allNodes 所有节点（用于碰撞检测）
 * @param {*} allEdges 所有边（用于碰撞检测）
 */
export function setBranchNodes(doneNodes = [], beRelatNodes = [], doneDataIds = [], allNodes = [], allEdges = []) {
  if (!beRelatNodes.length) return

  // 从已经完成的节点中寻找未完成的节点的关系
  // 创建一个Map来跟踪每个父节点已分配的分支位置
  const parentNodeBranchPositions = new Map()

  doneNodes.forEach((done) => {
    // 寻找和当前节点有关联的未处理节点
    const relatesDataIds = beRelatNodes
      .filter((be) => !doneDataIds.includes(be.dataId))
      .filter((be) => be.prevDataId === done.dataId || be.nextDataId === done.dataId)
      .map((r) => r.dataId)

    if (!relatesDataIds.length) return

    // 找到当前节点的前驱节点，用于确定主干走向
    const prevRelateDoneNode = findValidPrevNode({ node: done, prevNodes: doneNodes, findRelate: true })

    // 如果找不到前驱节点，使用默认方向
    if (!prevRelateDoneNode) {
      console.warn('无法找到前驱节点，使用默认方向')
    }

    // 计算主干方向
    const mainDirection = calculateDirection(prevRelateDoneNode, done)

    // 查找同一主干上的其他节点，用于确定主干的整体走向
    const sameLineNodes = doneNodes.filter((n) => n.lineNum === done.lineNum)

    // 找到当前节点在主干上的前后节点
    const nextMainNode = findValidNextNode({ node: done, nextNodes: sameLineNodes })

    // 确定主干的整体走向趋势
    let trunkDirection = mainDirection
    if (prevRelateDoneNode && nextMainNode) {
      // 如果有前后节点，计算整体走向
      trunkDirection = calculateDirection(prevRelateDoneNode, nextMainNode)
    }

    // 初始化当前父节点的已分配位置数组
    if (!parentNodeBranchPositions.has(done.id)) {
      parentNodeBranchPositions.set(done.id, [])
    }
    const usedPositions = parentNodeBranchPositions.get(done.id)
    const direction = calculateDirection(prevRelateDoneNode, done)
    // 计算分支节点坐标
    beRelatNodes.forEach((r, index) => {
      if (!relatesDataIds.includes(r.dataId)) {
        return
      }
      // TODO: 分支节点位置生成设计
      const distance = 100 // 连线距离
      const baseAngle = {
        N: 90, // 北
        S: 270, // 南
        E: 0, // 东
        W: done?.extraInfo?.percent > 0.3 ? 360 : 180, // 西
        ES: 45, // 东南
        WS: 135, // 西南
        EN: 315, // 东北
        // WN: 225, // 西北
        WN: 90, // 西北
      }
      // 计算角度区间
      const offset = 45
      const angleInterval = 45 // 45度一个区间
      // 调整角度，避开主干节点的垂直方向
      const startAngle = 35
      const angle = baseAngle[direction] + startAngle + index * angleInterval
      const newPos = calculateNewCoordinate(done, distance, angle)
      // TODO:针对生成的节点坐标进行碰撞检测查找当前
      r.x = newPos.x
      r.y = newPos.y
      r.angle = angle
    })
    // 处理完后继续递归处理，直到都设置好坐标
    const newDone = beRelatNodes.filter((r) => relatesDataIds.includes(r.dataId))
    const nextBeRelatNodes = beRelatNodes.filter((r) => !relatesDataIds.includes(r.dataId))
    doneDataIds.push(...relatesDataIds)

    // 更新allNodes以包含新处理的节点
    const updatedAllNodes = [...allNodes, ...newDone]

    setBranchNodes([...doneNodes, ...newDone], nextBeRelatNodes, doneDataIds, updatedAllNodes, allEdges)
  })
}
/**
 * 检测分支节点的闭环状态并补充相关信息。闭环是指分支节点通过某种方式最终连接回主干线路的情况。
 * 函数处理三种闭环场景：
 * 1. 直接闭环：分支节点直接连接两个主干节点
 * 2. 单向连接主干：分支节点的前驱或后继节点链中有一个连接到主干
 * 3. 双向连接主干：分支节点的前驱和后继节点链都连接到主干
 *
 * @param {Object} params - 参数对象
 * @param {Array<Object>} params.mainDataNodes - 主干节点数组，每个节点对象包含id属性
 * @param {string|null} params.prevDataId - 当前节点的前驱节点ID
 * @param {string|null} params.nextDataId - 当前节点的后继节点ID
 * @param {Object} params.lineObj - 线路对象
 * @param {Array<Object>} params.lineObj.nodes - 线路中的所有节点数组
 * @param {Array<Object>} params.lineObj.edges - 线路中的所有边数组，每个边对象包含source和target属性
 *
 * @returns {Object} 闭环相关信息
 * @returns {string|undefined} returns.mainTrunkNodeId - 如果分支节点直接连接到主干，返回对应的主干节点ID
 * @returns {boolean} returns.isClosedLoop - 是否形成闭环
 */
export function supplyBranchNodeInfo({ mainDataNodes, prevDataId, nextDataId, lineObj }) {
  // 检测是否为闭环分支（分支节点连接回主干）
  // 检查前驱节点是否在主干上
  const prevInMainTrunk = prevDataId && mainDataNodes.some((node) => node.id === prevDataId)
  // 检查后继节点是否在主干上
  const nextInMainTrunk = nextDataId && mainDataNodes.some((node) => node.id === nextDataId)

  // 如果前驱或后继节点在主干上，记录主干节点ID
  const mainTrunkNodeId = prevInMainTrunk ? prevDataId : nextInMainTrunk ? nextDataId : undefined

  // 标记是否为闭环分支
  // 检查分支节点是否在闭环路径中
  let isClosedLoop = false
  if (prevInMainTrunk && nextInMainTrunk) {
    // 当前分支节点的前后节点都是主干节点（一个分支节点时的闭环分支）
    // 直接连接主干的情况
    isClosedLoop = true
  } else {
    // 当前分支节点的前后节点不全是主干节点时：
    const prevNode = lineObj.nodes.find((n) => n.id === prevDataId)
    const nextNode = lineObj.nodes.find((n) => n.id === nextDataId)
    if (prevNode && !nextInMainTrunk) {
      // 当前节点的前驱节点是主干分支，但是后继节点是分支节点，检查它的所有后继节点是否存在主干分支
      const nextNodes = []
      let currentNode = nextNode
      while (currentNode) {
        nextNodes.push(currentNode)
        const nextId = lineObj.edges.find((e) => e.source === currentNode.id)?.target
        currentNode = lineObj.nodes.find((n) => n.id === nextId)
      }
      isClosedLoop = nextNodes.some((n) => mainDataNodes.some((m) => m.id === n.id))
    } else if (nextNode && !prevInMainTrunk) {
      // 当前节点的后继节点是主干分支，当前驱节点是分支节点，检查它的所有前驱节点是否存在主干分支
      const prevNodes = []
      let currentNode = prevNode
      while (currentNode) {
        prevNodes.push(currentNode)
        const prevId = lineObj.edges.find((e) => e.target === currentNode.id)?.source
        currentNode = lineObj.nodes.find((n) => n.id === prevId)
      }
      isClosedLoop = prevNodes.some((n) => mainDataNodes.some((m) => m.id === n.id))
    } else if (!prevInMainTrunk && !nextInMainTrunk) {
      // 当前节点前后节点都是分支节点时，需要分别检查前驱和后继节点链是否都连接到主干节点
      let prevConnected = false
      let nextConnected = false

      // 检查前驱节点链
      let currentNode = prevNode
      while (currentNode) {
        if (mainDataNodes.some((m) => m.id === currentNode.id)) {
          prevConnected = true
          break
        }
        const prevId = lineObj.edges.find((e) => e.target === currentNode.id)?.source
        currentNode = lineObj.nodes.find((n) => n.id === prevId)
      }

      // 检查后继节点链
      currentNode = nextNode
      while (currentNode) {
        if (mainDataNodes.some((m) => m.id === currentNode.id)) {
          nextConnected = true
          break
        }
        const nextId = lineObj.edges.find((e) => e.source === currentNode.id)?.target
        currentNode = lineObj.nodes.find((n) => n.id === nextId)
      }

      isClosedLoop = prevConnected && nextConnected
    }
  }
  return {
    // 添加闭环分支相关属性
    mainTrunkNodeId,
    isClosedLoop,
  }
}
